
require 'lfs'
require 'struct'

local cab_id = 'CABINET'
local cab_header = "c" .. #cab_id .. "II"
local cab_size = struct.size(cab_header)

local cabinet
do
    local exefile = assert(io.open(exec.path, 'rb'))
    
    exefile:seek('end', -cab_size)
    
    local test_id, chunk_pos, chunk_len = struct.unpack(cab_header, exefile:read(cab_size))
    
    if (test_id == cab_id) then
        exefile:seek('set', chunk_pos)
        cabinet = exefile:read(chunk_len)
        cabinet = loadstring(cabinet)
    end
end

local cabinet_scope = {exec_path = exec.path}

local cabinet_root

local folder_meta
folder_meta = {
    __call = function(folder, path)
        local cab,rest = path:match("^(.-)::()")
        if cab then
            folder = cabinet_root[cab:upper()]
            path = path:sub(rest)
        end
        for start,part in path:gmatch("()[\\/]?([^\\/]+)") do
            if (getmetatable(folder) ~= folder_meta) then
                error("not a folder: " .. path:sub(1,start-1), 2)
            end
            folder = folder[part:upper()]
            if not folder then
                return nil
            end
        end
        return folder
    end;
	__index = {
		sibling = function(self, ...)
			return self[".."](...);
		end;
		exists = function(self, ...)
			return not not self(...);
		end;
	}
}

local file_meta = {
    __call = function(self, ...)
        return self.folder(...)
    end;
	__index = {
		sibling = function(self, ...)
			return self.folder(...);
		end;
		exists = function(self, ...)
			return not not self.folder(...);
		end;
	}
}

function cabinet_scope.folder(t)
    t["."] = t
    for k,v in pairs(t) do
        if (getmetatable(v) == folder_meta) then
            v[".."] = t
        else
            v.folder = t
        end
    end
    return setmetatable(t, folder_meta)
end

function cabinet_scope.file(t)
    return setmetatable(t, file_meta)
end

local run_script

if cabinet then
	setfenv(cabinet, cabinet_scope)
	cabinet_root,run_script = cabinet()
else
	local path_base = getprojectbasepath()

	if not path_base then
	    return
	end
    
    local build_script = assert(loadfile(path_base .. ".build.lua"))
    
    local root = runbuildscript(build_script)

	local processdir
	function processdir(dir)
		local newdir = {}
		for name,child in pairs(dir) do
			if type(child) == 'string' then
				newdir[name:upper()] = setmetatable({path = child, folder = newdir, name = name}, file_meta)
			else
				local subdir = processdir(child)
				newdir[name:upper()] = subdir
				subdir[".."] = newdir
				subdir.name = name
			end
		end
		newdir["."] = newdir
		return setmetatable(newdir, folder_meta)
	end
	
	cabinet_root = processdir(root)
	run_script = path_base:upper() .. '.RUN.LUA'
	
end

do
    local scriptcache = setmetatable({}, {__mode='kv'})
    function loadscript(t)
		if (t == nil) then
			error('script file not found',2)
		end
        local cached = scriptcache[t]
        if cached then
            return cached
        end
        local file = io.open(t.path, 'rb')
		local start = t.offset or 0
        file:seek('set', start)
		local compiled = file:read(#('\027Lua')) == '\027Lua'
        file:seek('set', start)
        local script
		if compiled then
			script = file:read(t.length or '*a')
		else
			script = table.concat{"local F=...; return function(...) ", file:read(t.length or '*a'), "\nend"}
		end
        file:close()
        script = assert(loadstring(script, t.name))
        script = script(t)
        scriptcache[t] = script
        return script
    end
    function runscript(t, ...)
		if (t == nil) then
			error('script file not found',2)
		end
        return loadscript(t)(...)
    end
end

require 'image';	require 'video';	require 'time';		require 'audio'; 	require 'audio.retro'
require 'input';	require 'window';	require 'mouse'; 	require 'font';		require 'key'
require 'draw'

runscript(cabinet_root.PROJECT[run_script])
